/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../macro/head.h"
#include "../time/head.h"
#include "../xos/head.h"
#include "file_impl.h"
#include "dir_impl.h"

/////////////////////////////////////////////////////////////////////////////////////////
// 
namespace xos
{
    ////////////////////////////////////////////////////////////////////////////////////
    // 

    static dir_impl::ITEM_DATA xos_item_data;

    ////////////////////////////////////////////////////////////////////////////////////

    dir_impl::dir_impl()
    {
    }

    dir_impl::~dir_impl()
    {
        term();
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    int dir_impl::get_xos_item_data( ITEM_DATA*& item_ptr )
    {
        int ret = 0;
        xos_item_data.set_container( mgr::container() );
        item_ptr = &xos_item_data;
        return ret;
    }

    int dir_impl::init()
    {
        int nRet = 0;
        return nRet;
    }

    int dir_impl::term()
    {
        int nRet = 0;
        return nRet;
    }

    //////////////////////////////////////////////////////////////////////////////////
    // 
    // get full file name path
    // 

    int dir_impl::get_file_path( char * lpszPath, int nLen, const char * lpszFile )
    {
        int nRet = 0;

        const char * p = 0;
        int len = 0;

        if( 0 == nRet )
        {
            if( !lpszPath || ( 0 == lstrlenA( lpszPath ) ) || !lpszFile || ( 0 == lstrlenA( lpszFile ) ) )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            p = StrRChrA( lpszFile, 0, '\\' );

            if( !p )
            {
                nRet = 1;
            }
            else
            {
                len = ( int )( p - lpszFile );
            }
        }

        if( 0 == nRet )
        {
            if( nLen <= len )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            lstrcpynA( lpszPath, lpszFile, len );
        }

        return nRet;
    }

    // 
    // create dir and sub dirs
    // 
    int dir_impl::create_directory( const char * lpszPath )
    {
        int nRet = 0;

        int nResult = SHCreateDirectoryExA( 0, lpszPath, 0 );

        if( ( ERROR_SUCCESS == nResult ) || ( ERROR_ALREADY_EXISTS == nResult ) )
        {
            nRet = 0;
        }

        return nRet;
    }

    // 
    // delete dir and sub dirs
    // 
    int dir_impl::delete_directory( const char * lpszPath )
    {
        int nRet = 0;

        HANDLE hFind = INVALID_HANDLE_VALUE;
        WIN32_FIND_DATAA fData = { 0 };
        BOOL bFind = TRUE;
        char path[4096] = { 0 };

        strcpy_s( path, lpszPath );
        PathAppendA( path, "*" );

        hFind = FindFirstFileA( path, &fData );
        bFind = INVALID_HANDLE_VALUE != hFind;

        while( bFind )
        {
            char file[4096] = { 0 };

            strcpy_s( file, path );
            PathAppendA( file, fData.cFileName );

            if( fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
            {
                if( ( 0 != strcmp( fData.cFileName, "." ) ) && ( 0 != strcmp( fData.cFileName, ".." ) ) )
                {
                    delete_directory( file );
                }
            }
            else
            {
                delete_file( file );
            }

            bFind = FindNextFileA( hFind, &fData );
        }

        RemoveDirectoryA( path );

        if( INVALID_HANDLE_VALUE != hFind )
        {
            FindClose( hFind );
            hFind = INVALID_HANDLE_VALUE;
        }

        return nRet;
    }

    // 
    // if a dir exists
    // 
    int dir_impl::is_dir_exist( const char * lpszPath )
    {
        int nRet = 0;

        HANDLE hFind = INVALID_HANDLE_VALUE;
        WIN32_FIND_DATAA fData = { 0 };

        hFind = FindFirstFileA( lpszPath, &fData );

        if( ( INVALID_HANDLE_VALUE == hFind ) || ( FILE_ATTRIBUTE_DIRECTORY != ( fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) )
        {
            nRet = 1;
        }
        
        if( INVALID_HANDLE_VALUE != hFind )
        {
            FindClose( hFind );
            hFind = INVALID_HANDLE_VALUE;
        }

        return nRet;
    }

    //////////////////////////////////////////////////////////////////////////////////// 
    
    // 
    // scan all dirs and files into property:
    // 
    //   dirs : *xos_box::i_str_property
    //   file : *i_file
    // 
    // nDepth : 
    //    -1  : no limit
    //     0  : no scan
    //  other : depth
    // 
    // if lpszPath do not exitst, *ppv is null
    // 
    int dir_impl::scan_dir_and_files( enumFileType eType, const char * lpszExtFilter, const char* lpszPath, xos_box::i_str_property** ppv, int nDepth )
    {
        int ret = 0;

        xos_common::i_misc * pComMisc = mgr::container()->common()->misc();
        xos_box::i_string * pString = mgr::container()->box()->string();
        xos::i_crt * pCrt = mgr::container()->crt();

        // 
        // find
        // 
        HANDLE hFind = INVALID_HANDLE_VALUE;
        WIN32_FIND_DATAA fData = { 0 };
        BOOL bFind = FALSE;

        xos_box::i_str_property* pProperty = 0;
        bool bFile = eType & FILE_TYPE_FILE;
        bool bDir = eType & FILE_TYPE_DIR;

        {
            pProperty = mgr::container()->box()->get_str_prop();
            *ppv = pProperty;
        }

        if( 0 != nDepth )
        {
            pString->set( lpszPath );
            mgr::get()->misc()->path_append( pString, "*");
            hFind = FindFirstFileA( pString->get(0, 0), &fData);
            bFind = INVALID_HANDLE_VALUE != hFind;
            nDepth--;
        }

        while( bFind )
        {
            if( fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
            {
                if( (0 != strcmp( fData.cFileName, "." )) && (0 != strcmp( fData.cFileName, ".." )) && bDir )
                {
                    pString->set( lpszPath );
                    mgr::get()->misc()->path_append( pString, fData.cFileName );
                    xos_box::i_str_property* pProp = 0;
                    scan_dir_and_files( eType, lpszExtFilter, pString->get( 0, 0 ), &pProp, nDepth );
                    pProperty->set_str_prop( fData.cFileName, pProp, true );
                    pProp = 0;
                }
            }
            else if( bFile )
            {
                bool bSelectThisFile = true;
                if( lpszExtFilter )
                {
                    bSelectThisFile = false;
                    const char * lpszExt = pComMisc->ext( fData.cFileName, 0 );
                    if( lpszExt && ( 0 == pCrt->strcmp( lpszExt, lpszExtFilter ) ) )
                    {
                        bSelectThisFile = true;
                    }
                }
                if( bSelectThisFile )
                {
                    file_impl::T * p = 0;
                    xos_stl::init_pool_item( p );
                    {
                        FILETIME & ftime = fData.ftCreationTime;
                        int64_t tm = ftime.dwHighDateTime;
                        tm = ( tm << 32 ) | ftime.dwLowDateTime;
                        p->m_cTime = time_impl::file_1601_to_time_1970( tm );
                    }
                    {
                        FILETIME & ftime = fData.ftLastAccessTime;
                        int64_t tm = ftime.dwHighDateTime;
                        tm = ( tm << 32 ) | ftime.dwLowDateTime;
                        p->m_aTime = time_impl::file_1601_to_time_1970( tm );
                    }
                    {
                        FILETIME & ftime = fData.ftLastWriteTime;
                        int64_t tm = ftime.dwHighDateTime;
                        tm = ( tm << 32 ) | ftime.dwLowDateTime;
                        p->m_mTime = time_impl::file_1601_to_time_1970( tm );
                    }
                    p->m_file_name = fData.cFileName;
                    pProperty->set_obj( fData.cFileName, p, true );
                    p = 0;
                }
            }

            bFind = FindNextFileA( hFind, &fData );
        }

        if( INVALID_HANDLE_VALUE != hFind )
        {
            FindClose( hFind );
            hFind = INVALID_HANDLE_VALUE;
        }

        xos_stl::release_interface( pString );

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////

    // 
    // FILETIME and time(), F and T : F / 10000000 - 11644473600 = T
    // 
    // get file size and time
    // 
    int dir_impl::get_file_size_and_time( const char * lpszFile, int64_t * lpSize64, int64_t * lpCreateTime, int64_t * lpAccessTime, int64_t * lpModifyTime )
    {
        int ret = 0;

        WIN32_FIND_DATAA fData;

        HANDLE hFind = FindFirstFileA( lpszFile, &fData );

        if( INVALID_HANDLE_VALUE == hFind )
        {
            return 1;
        }
        else
        {
            FindClose( hFind );
            hFind = INVALID_HANDLE_VALUE;
        }

        if( lpCreateTime )
        {
            FILETIME & ftime = fData.ftCreationTime;
            int64_t tm = ftime.dwHighDateTime;
            tm = ( tm << 32 ) | ftime.dwLowDateTime;
            *lpCreateTime = time_impl::file_1601_to_time_1970( tm );
        }

        if( lpAccessTime )
        {
            FILETIME & ftime = fData.ftLastAccessTime;
            int64_t tm = ftime.dwHighDateTime;
            tm = ( tm << 32 ) | ftime.dwLowDateTime;
            *lpAccessTime = time_impl::file_1601_to_time_1970( tm );
        }

        if( lpModifyTime )
        {
            FILETIME & ftime = fData.ftLastWriteTime;
            int64_t tm = ftime.dwHighDateTime;
            tm = ( tm << 32 ) | ftime.dwLowDateTime;
            *lpModifyTime = time_impl::file_1601_to_time_1970( tm );
        }

        if( lpSize64 )
        {
            int64_t sz = fData.nFileSizeHigh;
            sz = sz << 32 | fData.nFileSizeLow;
            *lpSize64 = sz;
        }

        return ret;
    }

    // 
    // 设置文件时间。linux32下create time无效
    // 
    int dir_impl::set_file_time( const char * lpszFile, int64_t cTime, int64_t aTime, int64_t mTime )
    {
        int ret = 0;

        cTime = time_impl::time_1970_to_file_1601( cTime );
        aTime = time_impl::time_1970_to_file_1601( aTime );
        mTime = time_impl::time_1970_to_file_1601( mTime );

        HANDLE hFile = INVALID_HANDLE_VALUE;

        FILETIME c = { 0 };
        FILETIME a = { 0 };
        FILETIME m = { 0 };

        if( 0 == ret )
        {
            c.dwLowDateTime = cTime & 0xffffffff;
            c.dwHighDateTime = cTime >> 32;
            a.dwLowDateTime = aTime & 0xffffffff;
            a.dwHighDateTime = aTime >> 32;
            m.dwLowDateTime = mTime & 0xffffffff;
            m.dwHighDateTime = mTime >> 32;
        }
        
        if( 0 == ret )
        {
            hFile = CreateFileA( lpszFile, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
            if( INVALID_HANDLE_VALUE == hFile )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            bool bRet = SetFileTime( hFile, &c, &a, &m );
            if( !bRet )
            {
                ret = 1;
            }
        }

        CloseHandle( hFile );
        hFile = INVALID_HANDLE_VALUE;

        return ret;
    }

    // 
    // if a file exists
    // 
    int dir_impl::is_file_exist( const char * lpszFileName )
    {
        int nRet = 0;

        HANDLE hFind = INVALID_HANDLE_VALUE;
        WIN32_FIND_DATAA fData = { 0 };

        hFind = FindFirstFileA( lpszFileName, &fData );

        if( ( INVALID_HANDLE_VALUE == hFind ) || ( FILE_ATTRIBUTE_DIRECTORY == ( fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) )
        {
            nRet = 1;
        }

        if( INVALID_HANDLE_VALUE != hFind )
        {
            FindClose( hFind );
            hFind = INVALID_HANDLE_VALUE;
        }

        return nRet;
    }

    // 
    // delete a file
    // 
    int dir_impl::delete_file( const char * lpszFileName )
    {
        int nRet = 0;

        if( !DeleteFileA( lpszFileName ) )
        {
            nRet = 1;
        }

        return nRet;
    }

    // 
    // rename a file
    // 
    int dir_impl::rename_file( const char * lpszDesName, const char * lpszSrcName )
    {
        int nRet = 0;

        if( !MoveFileA( lpszSrcName, lpszDesName ) )
        {
            nRet = 1;
        }

        return nRet;
    }
} // xos
